package segmentTree;

import java.util.Arrays;

public class SegmentTree<E> {

    private E[] data;
    private E[] tree;
    private Merger<E> merger;

    public SegmentTree(E[] arr, Merger<E> merger) {

        this.merger = merger;

        data = (E[]) (new Object[arr.length]);
        for (int i = 0; i < arr.length; i++) {
            data[i] = arr[i];
        }

        tree = (E[]) (new Object[arr.length * 4]);
        buildSegmentTree(0, 0, data.length - 1);

    }

    //在treeIndex的位置上创建表示区间[l...r]的线段树
    private void buildSegmentTree(int treeIndex, int l, int r) {

        if (l == r) {
            tree[treeIndex] = data[l];
            return;
        }

        int mid = l + (r - l) / 2;
        int leftChild = leftChild(treeIndex);
        int rightChild = rightChild(treeIndex);


        buildSegmentTree(leftChild, l, mid);
        buildSegmentTree(rightChild, mid + 1, r);

        tree[treeIndex] = merger.merge(tree[leftChild], tree[rightChild]);

    }

    //返回节点的左孩子的索引
    private int leftChild(int treeIndex) {
        return 2 * treeIndex + 1;
    }

    //返回节点的右孩子的索引
    private int rightChild(int treeIndex) {
        return 2 * treeIndex + 2;
    }

    public E query(int leftIndex, int rightIndex) {
        if (leftIndex < 0 || leftIndex >= tree.length
                || rightIndex < 0 || rightIndex >= tree.length || leftIndex > rightIndex) {
            throw new IllegalArgumentException("Index is illegal");
        }

        return query(0, 0, data.length - 1, leftIndex, rightIndex);

    }

    //在以treeIndex为根的线段树[l...r]中，搜索区间[queryL...queryR]的值
    private E query(int treeIndex, int l, int r, int queryL, int queryR) {

        if (l == queryL && r == queryR) {
            return tree[treeIndex];
        }

        int mid = l + (r - l) / 2;
        if (queryR <= mid) {

            return query(leftChild(treeIndex), l, mid, queryL, queryR);

        } else if (queryL >= mid + 1) {

            return query(rightChild(treeIndex), mid + 1, r, queryL, queryR);

        }

        E leftResult = query(leftChild(treeIndex), l, mid, queryL, mid);
        E rightResult = query(rightChild(treeIndex), mid + 1, r, mid + 1, queryR);
        return merger.merge(leftResult, rightResult);

    }


    //将线段树中index的设为e
    public void set(int index, E e) {

        if (index < 0 || index >= data.length) {
            throw new IllegalArgumentException("Index is illegal");
        }
        data[index] = e;
        set(0, index, 0, data.length - 1, e);

    }

    private void set(int treeIndex, int index, int leftIndex, int rightIndex, E e) {

        if (leftIndex == rightIndex) {
            tree[treeIndex] = e;
            return;
        }

        int mid = leftIndex + (rightIndex - leftIndex) / 2;
        int leftChild = leftChild(treeIndex);
        int rightChild = rightChild(treeIndex);
        if (index >= mid + 1) {
            set(rightChild, index, mid + 1, rightIndex, e);

        } else {
            set(leftChild, index, leftIndex, mid, e);
        }

        tree[treeIndex] = merger.merge(tree[leftChild], tree[rightChild]);

    }

    @Override
    public String toString() {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("Segment: [");
        for (int i = 0; i < tree.length; i++) {
            if (tree[i] != null) {
                stringBuilder.append(tree[i]);
            } else {
                stringBuilder.append("null");
            }

            if (i != tree.length - 1) {
                stringBuilder.append(",");
            }
        }

        stringBuilder.append("]");
        return stringBuilder.toString();
    }
}
